`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 本模负责从SDRAM中读取逐点色度/亮度调整数据
* - 一次读取一行调整参数，一行最大为256个像素点
*/

/* NOTE:
* 亮度调整模式下:
* - 一行读取像素数n个32bit数据
* - 每个32bit数据包含R系数11bit，G系数11bit，B系数11bit
* - 两行调整系数在sdram中的偏移地址为256（1行）
*
* 色度调整模式下:
* - 一行读取像素数n个96bit数据
* - 每个96bit数据分为3个32bit，对应产生R、G、B所需的3组系数
* - 每组系数的32bit包含R系数11bit，G系数11bit，B系数11bit
* - 两行调整系数在sdram中的偏移地址为768（3行)
*
* 在内部ram中，总是将数据按照色度调整模式填充
* 读取时，需要考虑列控的情况，参考line_buf模块
*
* 仿真时发现，burst读的最后一个word并不能保持一个完整的周期
* 担心实际sdram行为与此类似，导致最后一个数据不能正确读取
* 所以不使用burst读，而是每个周期读一个数据
* 即总是用新的读命令打断前一个读命令
*
* 在SDRAM中保存的三帧数据分配存放在row[10:9]=0,1,2的区域
* 所以逐点调整数据只能存放在row[10:9]=3的区域
*
* 2018.02.28
* 在行模式下，每次总是处理8的倍数个像素点，当模组宽度不是8的倍数，
* 并且使用了多开模式的时候，外部模块实际读取的系数数量会大于实际模组宽度
* 比如模组宽度为60，实际会读取64个像素的系数，导致4个有效系数被丢弃
* 使用“分区宽度”来进行修正
*/

module coe_reader (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_sdram_clk, // 150M
    input  wire         I_rst_n,
   
   
    input  wire         I_coe_ram_clk,
    input  wire         I_coe_ram_wren_r,
    input  wire         I_coe_ram_wren_g,
    input  wire         I_coe_ram_wren_b,
    input  wire[31:0]   I_coe_ram_data,
    input  wire[7:0]    I_coe_ram_addr,
   
    // config
    input  wire         I_cfg_coe_mode,     // 0 - 色度调整, 1 - 亮度调整
    input  wire [10:0]  I_cfg_scan_length, // 一扫的像素数量（包含虚点）
    input  wire [1:0]   I_cfg_box_dir,
    // read request
    input  wire         I_coe_load_req,
   
    input  wire         I_coe_ack,
    output wire [15:0]  O_coe_r,
    output wire [15:0]  O_coe_g,
    output wire [15:0]  O_coe_b
);
//------------------------Local signal-------------------
// box direction
localparam [1:0]
    LANDSCAPE = 0, // 横向
    PORTRAIT0 = 1, // 纵向，第一个端口在左侧
    PORTRAIT1 = 2; // 纵向，第一个端口在右侧



reg  [95:0] ram_data;
wire        ram_rden;
reg  [7:0]  ram_raddr;
wire [95:0] ram_q;
reg  [2:0]  pre_read_sr;
// write ram
reg  [2:0]  word_sel;
reg  [1:0]  coe_rgb_cnt;
reg  [7:0]  data_cnt;
// read ram
reg  [31:0] coe_buf;
reg  [2:0]  coe_sel;


// sdpram_256x32  b
sdpram_256x32 ram0 (/*{{{*/
    .data      ( {I_coe_ram_data[ 7:0],I_coe_ram_data[ 15:8],I_coe_ram_data[ 23:16],I_coe_ram_data[ 31:24]} ),
    .rdaddress ( ram_raddr ),
    .rdclock   ( I_sclk ),
    .rden      ( ram_rden ),
    .wraddress ( I_coe_ram_addr ),
    .wrclock   ( I_coe_ram_clk ),
    .wren      ( I_coe_ram_wren_b ),
    .q         ( ram_q[31:0] )
);/*}}}*/

// ad_mem #(
    // .DATA_WIDTH     (32)     ,
    // .ADDRESS_WIDTH  (8) 
// )
// ram0(
    // .clka           ( I_coe_ram_clk ),
    // .wea            ( I_coe_ram_wren_b ),
    // .addra          ( I_coe_ram_addr ),
    // .dina           ( {I_coe_ram_data[ 7:0],I_coe_ram_data[ 15:8],I_coe_ram_data[ 23:16],I_coe_ram_data[ 31:24]} ),

    // .clkb           ( I_sclk ),
    // .reb            ( ram_rden ),
    // .addrb          ( ram_raddr ),
    // .doutb          ( ram_q[31:0] )
// );

// sdpram_256x32  g
sdpram_256x32 ram1 (/*{{{*/
    .data      ( {I_coe_ram_data[ 7:0],I_coe_ram_data[ 15:8],I_coe_ram_data[ 23:16],I_coe_ram_data[ 31:24]} ),
    .rdaddress ( ram_raddr ),
    .rdclock   ( I_sclk ),
    .rden      ( ram_rden ),
    .wraddress ( I_coe_ram_addr ),
    .wrclock   ( I_coe_ram_clk ),
    .wren      ( I_coe_ram_wren_g ),
    .q         ( ram_q[63:32] )
);/*}}}*/


// ad_mem #(
    // .DATA_WIDTH     (32)     ,
    // .ADDRESS_WIDTH  (8) 
// )
// ram1(
    // .clka           ( I_coe_ram_clk ),
    // .wea            ( I_coe_ram_wren_g ),
    // .addra          ( I_coe_ram_addr ),
    // .dina           ( {I_coe_ram_data[ 7:0],I_coe_ram_data[ 15:8],I_coe_ram_data[ 23:16],I_coe_ram_data[ 31:24]} ),

    // .clkb           ( I_sclk ),
    // .reb            ( ram_rden ),
    // .addrb          ( ram_raddr ),
    // .doutb          ( ram_q[63:32] )
// );

// sdpram_256x32  r
sdpram_256x32 ram2 (/*{{{*/
    .data      ( {I_coe_ram_data[ 7:0],I_coe_ram_data[ 15:8],I_coe_ram_data[ 23:16],I_coe_ram_data[ 31:24]}  ),
    .rdaddress ( ram_raddr ),
    .rdclock   ( I_sclk ),
    .rden      ( ram_rden ),
    .wraddress ( I_coe_ram_addr ),
    .wrclock   ( I_coe_ram_clk ),
    .wren      ( I_coe_ram_wren_r ),
    .q         ( ram_q[95:64] )
);/*}}}*/


// ad_mem #(
    // .DATA_WIDTH     (32)     ,
    // .ADDRESS_WIDTH  (8) 
// )
// ram2(
    // .clka           ( I_coe_ram_clk ),
    // .wea            ( I_coe_ram_wren_r ),
    // .addra          ( I_coe_ram_addr ),
    // .dina           ( {I_coe_ram_data[ 7:0],I_coe_ram_data[ 15:8],I_coe_ram_data[ 23:16],I_coe_ram_data[ 31:24]} ),

    // .clkb           ( I_sclk ),
    // .reb            ( ram_rden ),
    // .addrb          ( ram_raddr ),
    // .doutb          ( ram_q[95:64] )
// );
 

assign ram_rden = (I_coe_ack && coe_sel == 3'b010);

// ram_raddr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        ram_raddr <= 1'b0;
    else if ( I_coe_load_req) begin
        if (I_cfg_box_dir == PORTRAIT1)
            // NOTE: 列模式下，如果第一个端口在右边，则反着读取数据
            ram_raddr <= I_cfg_scan_length-1;
        else
            ram_raddr <= 1'b0;
    end
    else if (ram_rden) begin
        if (I_cfg_box_dir == PORTRAIT0) begin // 列模式，顺序
                ram_raddr <= ram_raddr + 'd1;
        end
        else if (I_cfg_box_dir == PORTRAIT1) begin // 列模式，逆序
                ram_raddr <= ram_raddr - 'd1;
        end
        else begin // 行模式
                ram_raddr <= ram_raddr + 1'b1;
        end
    end
end

reg coe_load_req_r;
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        coe_load_req_r <= 1'b0;
    else 
        coe_load_req_r <= I_coe_load_req;
end
// coe_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        coe_buf <= 1'b0;
    else if (coe_load_req_r)
        coe_buf <= ram_q[95:64];
    else if (I_coe_ack) begin
        if (coe_sel[2])
            coe_buf <= ram_q[63:32];
        else if (coe_sel[1])
            coe_buf <= ram_q[31:0];
        else
            coe_buf <= ram_q[95:64];
    end
end

// coe_sel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        coe_sel <= 1'b0;
    else if (coe_load_req_r)
        coe_sel <= 3'b100;
    else if (I_coe_ack)
        coe_sel <= {coe_sel[0], coe_sel[2:1]};
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++


assign O_coe_r = {coe_buf[31:21], 5'd0};
assign O_coe_g = {coe_buf[20:10], 5'd0};
assign O_coe_b = {coe_buf[9:0], 6'd0};
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
